package com.artisan.day01;

import dev.langchain4j.data.message.AiMessage;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.memory.ChatMemory;
import dev.langchain4j.memory.chat.TokenWindowChatMemory;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.model.openai.OpenAiTokenizer;
import dev.langchain4j.model.output.Response;

import static dev.langchain4j.data.message.UserMessage.userMessage;
import static dev.langchain4j.model.openai.OpenAiChatModelName.GPT_4_O_MINI;

public class MultiRoundOfDialog {

    public static void main(String[] args) {

        chatNoContent();
        System.out.println("======================");
        chatWithUserMessage();
        System.out.println("----------------------");
        chatWithChatMemory();

    }


    /**
     * 演示与OpenAI聊天模型的交互，但没有提供内容输入
     * 此方法展示了如何配置和使用OpenAI聊天模型的实例
     */
    public static void chatNoContent(){
        // 打印非正常版本的分隔符
        System.out.println("======NOT  OK VERSION========");

        // 构建OpenAI聊天模型实例
        OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
                .apiKey("demo")
                .modelName(GPT_4_O_MINI)
                .build();

        // 生成并打印模型对简单文本输入的响应
        System.out.println(openAiChatModel.generate("我是小工匠"));
        System.out.println(openAiChatModel.generate("请问，我叫什么名字？"));
    }


    /**
     * 与用户进行对话的消息示例方法
     * 该方法展示了如何使用OpenAI的聊天模型生成对话，以及如何在对话中传递上下文
     */
    public static void chatWithUserMessage() {
        // 构建OpenAI聊天模型实例
        OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
                .apiKey("demo")
                .modelName(GPT_4_O_MINI)
                .build();


        // 打印正常版本的分隔符
        System.out.println("========OK VERSION========");

        // 创建用户消息实例
        UserMessage userMessage1 = userMessage("你好，我是小工匠");
        // 生成并获取模型对用户消息的响应
        Response<AiMessage> response1 = openAiChatModel.generate(userMessage1);
        AiMessage aiMessage1 = response1.content(); // 大模型的第一次响应
        // 打印模型的响应文本
        System.out.println(aiMessage1.text());
        System.out.println("----");

        // 下面一行代码是重点
        // 通过在模型生成中包含上一次用户消息和AI响应，来维护对话上下文
        Response<AiMessage> response2 = openAiChatModel.generate(userMessage1, aiMessage1, userMessage("请问，我叫什么？"));
        AiMessage aiMessage2 = response2.content(); // 大模型的第二次响应
        // 打印模型的第二次响应文本
        System.out.println(aiMessage2.text());
    }



    /**
     * 演示如何在多轮对话中使用聊天记忆。
     * 该方法展示了如何使用聊天记忆与语言模型进行交互，包括如何向聊天记忆中添加用户消息和AI响应，以及如何控制对话流程。
     */
    private static void chatWithChatMemory() {
        // 初始化聊天记忆，设置最大令牌数为300，并使用OpenAI分词器。
        // 这个设置有助于控制对话长度，避免过多的令牌消耗。
        ChatMemory chatMemory = TokenWindowChatMemory.withMaxTokens(300, new OpenAiTokenizer());

        // 创建并配置语言模型。
        // 使用OpenAI提供的GPT-4-O-MINI模型，并使用演示API密钥。
        ChatLanguageModel model = OpenAiChatModel.builder()
                .apiKey("demo")
                .modelName(GPT_4_O_MINI)
                .build();

        // 你可以完全控制聊天记忆。可以决定是否将特定 5消息添加到记忆中
        // （例如，你可能不想存储少量样本以节省令牌）。 如果需要，你可以在保存之前处理或修改消息。

        // 向聊天记忆中添加用户消息。
        chatMemory.add(userMessage("你好, 我的名字是小工匠"));
        // 根据当前聊天记忆生成AI响应并打印。
        AiMessage answer = model.generate(chatMemory.messages()).content();
        System.out.println(answer.text());
        // 将AI的响应添加到聊天记忆中。
        chatMemory.add(answer);

        // 向聊天记忆中添加另一个用户消息。
        chatMemory.add(userMessage("我叫什么名字?"));
        // 根据更新后的聊天记忆再次生成AI响应并打印。
        AiMessage answerWithName = model.generate(chatMemory.messages()).content();
        System.out.println(answerWithName.text());
        // 将新的AI响应添加到聊天记忆中。
        chatMemory.add(answerWithName);
    }

}
